#include "types.h"
#include "defs.h"
#include "param.h"
#include "x86.h"
#include "mmu.h"
#include "proc.h"
#include "spinlock.h"
#include "binary_semaphore.h"

int binaryid = 1000;

void initQueue(struct queue* waiters){

	int i;
	for(i = 0; i < NPROC; i++){
		waiters->processes[i] = 0;
	}
	waiters->size = 0;
}

void enqueue(struct proc* p, struct queue* waiters){
	waiters->processes[waiters->size] = p;
	//cprintf("thread id in enqueue is %d\n", waiters->processes[waiters->size]->thread_id);
	(waiters->size)++;
	//cprintf("size of waiters in enque is: %d\n", waiters->size);
}

struct proc* dequeue(struct queue* waiters){
	struct proc* returnProc = 0;
	int i = 0;
	if(waiters->size <= 0){
		panic("queue is empty");
	}
	else{
		returnProc = waiters->processes[0];
		for(i = 1; i < waiters->size; i++){
			waiters->processes[i-1] = waiters->processes[i];
		}
		(waiters->size)--;
	}
	return returnProc;
}

struct proc* first(struct queue* waiters){
	struct proc* returnProc = 0;
	if(waiters->size <= 0){
		panic("queue is empty");
	}
	else{
		returnProc = waiters->processes[0];
	}
	return returnProc;
}

struct binSemaphores{
	struct semaphore semaphores[NPROC];
	struct spinlock lock;
};

struct binSemaphores binarySemaphores;

int binary_sem_create(){
	int i;
	int semaphoreID = -1;
	acquire(&binarySemaphores.lock);
	for(i = 0; i < NPROC; i++){
		if(binarySemaphores.semaphores[i].used == 0){
			semaphoreID = binaryid;
			binarySemaphores.semaphores[i].used = 1;
			binarySemaphores.semaphores[i].id = binaryid++;
			binarySemaphores.semaphores[i].value = 1;
			initQueue(&binarySemaphores.semaphores[i].q);
			break;
		}
	}
	if(i == NPROC){
		cprintf("no semaphore available");
		release(&binarySemaphores.lock);
		return -1;
	}
	release(&binarySemaphores.lock);
	return semaphoreID;
}

int binary_sem_down(int binary_sem_ID){
	int foundSemaphore = 0;
	struct semaphore *sem;
	int i;
	acquire(&binarySemaphores.lock);
	//cprintf("thread %d entered sem down and acquired semaphore table lock\n", proc->thread_id);
	for(i = 0; i < NPROC; i++){
		if(binarySemaphores.semaphores[i].id == binary_sem_ID){
			foundSemaphore = 1;
			sem = &binarySemaphores.semaphores[i];
			break;
		}
	}
	if(foundSemaphore == 0 ){
		cprintf("binary semaphore doesn't exist");
		release(&binarySemaphores.lock);
		return -1;
	}
	//cprintf("found semaphore in sem down. It's ID is %d\n", sem->id);

	//found the binary semaphore that matches the id given

        acquire(&sem->mutex);
	//cprintf("thread %d has acquired semaphore mutex lock\n",proc->thread_id);
	//cprintf("thread %d sees that the value of the semaphore is: %d\n",proc->thread_id, sem->value);
	if(sem->value == 1){
		sem->value = 0;
		//cprintf("thread %d set the value of the semaphore to 0\n",proc->thread_id);
		release(&sem->mutex);
		release(&binarySemaphores.lock);
	} else{
		//cprintf("thread %d tried to acquire mutex but failed\n", proc->thread_id);
		enqueue(proc, &sem->q);
		//cprintf("thread %d was enqueued and going to sleep\n", proc->thread_id);
		release(&binarySemaphores.lock);
		sleep(proc, &sem->mutex);
		//cprintf("thread %d woke up\n", proc->thread_id);
		release(&sem->mutex);
	}
	
	return 0;
}

int binary_sem_up(int binary_sem_ID){
	int foundSemaphore = 0;
	struct semaphore *sem;
	int i;
	acquire(&binarySemaphores.lock);
	//cprintf("thread %d entered sem up and acquired semaphore table lock\n", proc->thread_id);
	for(i = 0; i < NPROC; i++){
		if(binarySemaphores.semaphores[i].id == binary_sem_ID){
			foundSemaphore = 1;
			sem = &binarySemaphores.semaphores[i];
			break;
		}
	}
	if(foundSemaphore == 0){
		cprintf("binary semaphore doesn't exist");
		release(&binarySemaphores.lock);
		return -1;
	}
	//cprintf("found semaphore in sem up. It's ID is %d\n", sem->id);
	//found the binary semaphore that matches the id given
	
	acquire(&sem->mutex);
	//cprintf("thread %d has acquired semaphore mutex lock\n",proc->thread_id);
	//cprintf("thread %d sees that q size is: %d\n",proc->thread_id, sem->q.size);
	if(sem->q.size > 0){
		struct proc* p = dequeue(&sem->q);
		wakeup(p);
		release(&sem->mutex);
		//cprintf("thread %d dequeued thread %d, woke him up and released mutex\n",proc->thread_id, proc->thread_id);
	} else{
		sem->value = 1;
		release(&sem->mutex);
		//cprintf("thread %d saw that the queue size is less than 0, set the value to 1 and released mutex\n",proc->thread_id);
	}
	release(&binarySemaphores.lock);
	return 0;
} 
